#pragma once
#include <string>
#include <memory>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"

using namespace LogModule;

namespace SocketModule
{
    static const int defaultbacklog = 8;
    class Socket
    {
    public:
        ~Socket() = default;
        virtual void SocketOrDie() = 0;
        virtual void BindOrDie(const InetAddr &addr) = 0;
        virtual void ListenOrDie(int backlog) = 0;
        virtual std::shared_ptr<Socket> Accept(InetAddr *addr) = 0;
        virtual void Close() = 0;
        virtual ssize_t Recv(char *buff, size_t buffsize) = 0;
        virtual ssize_t Send(const std::string &data) = 0;
        virtual void ConnectOrDie(const InetAddr &addr) = 0;

        virtual void BuildTCPServerSocketMethod(const InetAddr &addr, int backlog = defaultbacklog)
        {
            SocketOrDie();
            BindOrDie(addr);
            ListenOrDie(backlog);
        }

        virtual void BuildTCPClientSocketMethod(const InetAddr &addr)
        {
            SocketOrDie();
        }
    };
    enum Type
    {
        SERVER,
        CLIENT
    };

    class TCPSocket : public Socket
    {
    public:
        TCPSocket(const InetAddr &addr, int type)
            : _sockfd(-1)
        {
            if (type == Type::SERVER)
                BuildTCPServerSocketMethod(addr);
            else if (type == Type::CLIENT)
                BuildTCPClientSocketMethod(addr);
            else
            {
                LOG(LogLevel::FATAL) << "type is not exist";
                exit(TYPE_ERR);
            }
        }
        TCPSocket(int sockfd)
            : _sockfd(sockfd)
        {
        }

        void SocketOrDie() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "socket error";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::INFO) << "socket success";
        }

        void BindOrDie(const InetAddr &addr) override
        {
            int n = bind(_sockfd, CONV(addr.Peer()), addr.Socklen());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind error";
                exit(BIND_ERR);
            }
            LOG(LogLevel::INFO) << "bind success";
        }

        void ListenOrDie(int backlog) override
        {
            int n = listen(_sockfd, backlog);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen error";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "listen success";
        }

        std::shared_ptr<Socket> Accept(InetAddr *addr) override
        {
            struct sockaddr_in peer;
            socklen_t len;
            int fd = accept(_sockfd, CONV(&peer), &len);
            if (fd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error";
                return nullptr;
            }
            // 将客户端IP+port带出去
            addr->SetAddr(peer);
            // 将fd带出去，并创建TCPSocket对象，可用于直接通信
            return std::make_shared<TCPSocket>(fd);
        }

        void Close() override
        {
            if (_sockfd > 0)
                close(_sockfd);
        }

        ssize_t Recv(char *buff, size_t buffsize) override
        {
            return recv(_sockfd, buff, buffsize - 1, 0);
        }

        ssize_t Send(const std::string &data) override
        {
            return send(_sockfd, data.c_str(), data.size(), 0);
        }

        void ConnectOrDie(const InetAddr &addr) override
        {
            int n = connect(_sockfd, CONV(addr.Peer()), addr.Socklen());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "connect error";
                exit(CONNECT_ERR);
            }
            LOG(LogLevel::INFO) << "connect success";
        }

        ~TCPSocket()
        {
        }

    private:
        int _sockfd; // 既可以是监听fd，也可以是通信fd
    };

}